String.prototype.encodeHtml = function(){
    return this.replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#34;');
};

String.prototype.decodeHtml = function(){
    return this
    .replace(/(&lt;)/g, '<')
    .replace(/(&gt;)/g, '>')
    .replace(/(&quot;)/g, '"')
    .replace(/(&#34;)/g, "'")
    .replace(/(&amp;)/g, '&');
};

String.prototype.stripHtml = function(){
    return this.replace(/<[^>]+>/g, '');
};

String.prototype.formatNumber = function(decimals) {
    var startIndex, output = '', thisStr = this, str = '', addDash = false;

    if (null === decimals || undefined === decimals) {
        decimals = 0;
    }

    if (decimals > 0) {
        var bridgeNum = 1;
        for (var i = 1; i <= decimals; i++) {
            bridgeNum *= 10;
        }
        thisStr = Math.round(thisStr*bridgeNum)/bridgeNum + '';
    }

    if ('-' == thisStr.charAt(0)) {
        addDash = true;
        str = thisStr.substring(1).replace(/[^0-9\.%]/g, '');
    } else {
        str = thisStr.replace(/[^0-9\.%-]/g, '');
    }
    
    var dotPos = str.indexOf('.');
    var pcPos = str.indexOf('%');
    if (dotPos > -1) {
        startIndex = dotPos - 1;
        output = str.substr(dotPos);
    } else if (pcPos > -1) {
        startIndex = pcPos - 1;
        output = str.substr(pcPos);
    } else {
        startIndex = str.length - 1;
    }
    var charCounter = 0;
    for (i = startIndex; i >= 0; i--) {
        charCounter++;
        output = str.charAt(i) + output;
        if (charCounter%3===0 && i > 0) {
            output = ',' + output;
        }
    }
    if (addDash) {
        output = '-' + output;
    }
    return output;
};

String.prototype.removeNumberFormat = function() {
    return this.replace(/,+/g, '');
};

// swap yyyy-mm-dd format and dd-mm-yyyy
String.prototype.swapDateFormat = function()
{
    var parts = this.split('-');
    return parts[2] + '-' + parts[1] + '-' + parts[0];
};

// convert a string yyyy-mm-dd to javascript Date
String.prototype.toDate = function()
{
    var parts = this.split('-');
    return new Date(parts[0], parts[1]-1, parts[2], 0, 0, 0);
};

Number.prototype.formatNumber = function(decimals) {
    return (this+'').formatNumber(decimals);
};

/*
 * Date Format 1.2.3
 * (c) 2007-2009 Steven Levithan <stevenlevithan.com>
 * MIT license
 *
 * Includes enhancements by Scott Trenda <scott.trenda.net>
 * and Kris Kowal <cixar.com/~kris.kowal/>
 *
 * Accepts a date, a mask, or a date and a mask.
 * Returns a formatted version of the given date.
 * The date defaults to the current date/time.
 * The mask defaults to dateFormat.masks.default.
 */

var dateFormat = function () {
    var	token = /d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZ]|"[^"]*"|'[^']*'/g,
    timezone = /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g,
    timezoneClip = /[^-+\dA-Z]/g,
    pad = function (val, len) {
        val = String(val);
        len = len || 2;
        while (val.length < len) val = "0" + val;
        return val;
    };

    // Regexes and supporting functions are cached through closure
    return function (date, mask, utc) {
        var dF = dateFormat;

        // You can't provide utc if you skip other args (use the "UTC:" mask prefix)
        if (arguments.length == 1 && Object.prototype.toString.call(date) == "[object String]" && !/\d/.test(date)) {
            mask = date;
            date = undefined;
        }

        // Passing date through Date applies Date.parse, if necessary
        date = date ? new Date(date) : new Date;
        if (isNaN(date)) throw SyntaxError("invalid date");

        mask = String(dF.masks[mask] || mask || dF.masks["default"]);

        // Allow setting the utc argument via the mask
        if (mask.slice(0, 4) == "UTC:") {
            mask = mask.slice(4);
            utc = true;
        }

        var	_ = utc ? "getUTC" : "get",
        d = date[_ + "Date"](),
        D = date[_ + "Day"](),
        m = date[_ + "Month"](),
        y = date[_ + "FullYear"](),
        H = date[_ + "Hours"](),
        M = date[_ + "Minutes"](),
        s = date[_ + "Seconds"](),
        L = date[_ + "Milliseconds"](),
        o = utc ? 0 : date.getTimezoneOffset(),
        flags = {
            d:    d,
            dd:   pad(d),
            ddd:  dF.i18n.dayNames[D],
            dddd: dF.i18n.dayNames[D + 7],
            m:    m + 1,
            mm:   pad(m + 1),
            mmm:  dF.i18n.monthNames[m],
            mmmm: dF.i18n.monthNames[m + 12],
            yy:   String(y).slice(2),
            yyyy: y,
            h:    H % 12 || 12,
            hh:   pad(H % 12 || 12),
            H:    H,
            HH:   pad(H),
            M:    M,
            MM:   pad(M),
            s:    s,
            ss:   pad(s),
            l:    pad(L, 3),
            L:    pad(L > 99 ? Math.round(L / 10) : L),
            t:    H < 12 ? "a"  : "p",
            tt:   H < 12 ? "am" : "pm",
            T:    H < 12 ? "A"  : "P",
            TT:   H < 12 ? "AM" : "PM",
            Z:    utc ? "UTC" : (String(date).match(timezone) || [""]).pop().replace(timezoneClip, ""),
            o:    (o > 0 ? "-" : "+") + pad(Math.floor(Math.abs(o) / 60) * 100 + Math.abs(o) % 60, 4),
            S:    ["th", "st", "nd", "rd"][d % 10 > 3 ? 0 : (d % 100 - d % 10 != 10) * d % 10]
        };

        return mask.replace(token, function ($0) {
            return $0 in flags ? flags[$0] : $0.slice(1, $0.length - 1);
        });
    };
}();

// Some common format strings
dateFormat.masks = {
    "default":      "ddd mmm dd yyyy HH:MM:ss",
    shortDate:      "m/d/yy",
    mediumDate:     "mmm d, yyyy",
    longDate:       "mmmm d, yyyy",
    fullDate:       "dddd, mmmm d, yyyy",
    shortTime:      "h:MM TT",
    mediumTime:     "h:MM:ss TT",
    longTime:       "h:MM:ss TT Z",
    isoDate:        "yyyy-mm-dd",
    isoTime:        "HH:MM:ss",
    isoDateTime:    "yyyy-mm-dd'T'HH:MM:ss",
    isoUtcDateTime: "UTC:yyyy-mm-dd'T'HH:MM:ss'Z'"
};

// Internationalization strings
dateFormat.i18n = {
    dayNames: [
    "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
    "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
    ],
    monthNames: [
    "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
    "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
    ]
};

// For convenience...
Date.prototype.format = function (mask, utc) {
    return dateFormat(this, mask, utc);
};

Date.prototype.roundByHours = function() {
    var hours = this.getHours();
    var minutes = this.getMinutes();
    if (minutes >= 30) {
        this.setHours(hours+1, 0, 0, 0);
    } else {
        this.setHours(hours, 0, 0, 0);
    }
    return this;
};

function arrayHasItem(value, arr) {
    for (var i = 0, loopCnt = arr.length; i < loopCnt; i++) {
        if (arr[i] === value) {
            return true;
        }
    }
    return false;
}

function arrayRemoveItem(item, arr) {
    for (var i = 0, loopCnt = arr.length; i < loopCnt; i++) {
        if (arr[i] === item) {
            arr.splice(i, 1);
            return true;
        }
    }
    return true;
}

function ltrim(str) {
    return str.replace(/^\s+/g, '');
}

function rtrim(str) {
    return str.replace(/\s+$/g, '');
}

function trim(str) {
    return ltrim(rtrim(str));
}

var Serializer = {
    serialize: function (obj) {
        if (null === obj) {
            return 'null';
        }
        switch (typeof obj) {
            case 'function':
                return obj.toString();
            case 'number':
            case 'boolean':
                return obj;
            case 'string':
                return '\'' + obj.replace(/\\/g, '\\').replace(/'/g, '\\\'') + '\'';
            case 'object':
                var str;
                if (Array === obj.constructor) {
                    str = '[';
                    var i, maxIndex = obj.length - 1;
                    for (i = 0; i <= maxIndex; i++) {
                        str += this.serialize(obj[i]);
                        if (i < maxIndex) {
                            str += ',';
                        }
                    }
                    str += this.serialize(obj[i]) + ']';
                } else if (Date === obj.constructor) {
                    str = 'new Date(' + obj.getTime() + ')';
                } else {
                    str = '{';
                    var key;
                    for (key in obj) {
                        str += key + ':' + this.serialize(obj[key]) + ',';
                    }
                    str = str.replace(/\,$/, '') + '}';
                }
                return str;
            default:
                return '';
        }
    },
    unserialize: function(str) {
        return eval('(' + str + ')');
    }
};

var Scheduler = {
    _timerId: null,
    _interval: 15,
    setInterval: function(interval) {
        this._interval = interval;
        return this;
    },
    add: function(command, params) {
        var tasks = this.getTasks();
        tasks.push({
            'command': command,
            'params': 'undefined' !== typeof params ? params : null
        });
        Cacher.set('tasks', tasks);
        if (1 == tasks.length) {
            this.counter(this._interval);
        }
        return this;
    },
    remove: function (index) {
        var arr = this.getTasks();
        if ('undefined' !== typeof arr[index]) {
            arr.splice(index, 1);
        }
        Cacher.set('tasks', arr);
        return this;
    },
    getTasks: function() {
        var tasks = Cacher.get('tasks');
        if (null === tasks) {
            tasks = [];
        }
        return tasks;
    },
    init: function() {
        if (this.getTasks().length > 0) {
            this.run();
        }
        return this;
    },
    run: function () {
        window.status = 'Running synchronization...';
        this._runCounter = this._interval;
        var tasks = this.getTasks();
        for (var counter = 0; counter < tasks.length; counter++) {
            var isOk = false;
            var task = tasks[counter];
            var command = task.command;
            if ('function' === typeof command) {
                var params = task.params;
                if (null !== params && Array !== params.constructor) {
                    params = [params];
                }
                isOk = command.apply(this, params);
            } else {
                isOk = eval(command);
            }
            if (isOk || 'undefined' === typeof isOk) {
                this.remove(counter);
                counter--;
            }
        }
        if (tasks.length < 1) {
            window.clearTimeout(this._timerId);
            window.status = 'Done';
        } else {
            this.counter(this._interval);
        }
        return this;
    },
    counter: function(secs) {
        window.clearTimeout(this._timerId);
        if (secs > 0) {
            window.status = secs + ' seconds until synchronizing...';
            this._timerId = setTimeout('Scheduler.counter(' + --secs + ')', 1000);
        } else {
            this.run();
        }
    }
};

var Cacher = {
    _values: [],
    set: function (key, value, addCookie) {
        this._values[key] = value;
        if (addCookie) {
            Cookie.set('_cache_' + key, Serializer.serialize(value), 10);
        }
        return this;
    },
    get: function (key) {
        if ('undefined' !== typeof this._values[key]) {
            return this._values[key];
        }
        return this.getCookieCache(key);
    },
    remove: function(key) {
        try {
            delete this._values[key];
            if (null !== this.getCookieCache(key)) {
                Cookie.clear('_cache_' + key);
            }
        } catch (e) {}
    },
    getCookieCache: function(key) {
        var cookie = Cookie.get('_cache_' + key);
        return null !== cookie ? Serializer.unserialize(cookie) : null;
    }
};

var Cookie = {
    set: function (name, value, days) {
        var expires = '';
        if (days) {
            var date = new Date();
            date.setTime(date.getTime() + (days*24*60*60*1000));
            expires = '; expires=' + date.toGMTString();
        }
        document.cookie = name + '=' + escape(value) + expires + '; path=/';
        return this;
    },
    get: function(name) {
        name += '=';
        var list = document.cookie.split(';');
        for (var i = 0; i < list.length; i++) {
            var item = ltrim(list[i]);
            if (item.indexOf(name) == 0) {
                return unescape(item.substring(name.length,item.length));
            }
        }
        return null;
    },
    clear: function(name) {
        return this.set(name, '', -1);
    }
};

var sprintfWrapper = {

    init : function () {
        if (typeof arguments == "undefined") {
            return null;
        }
        if (arguments.length < 1) {
            return null;
        }
        if (typeof arguments[0] != "string") {
            return null;
        }
        if (typeof RegExp == "undefined") {
            return null;
        }

        var string = arguments[0];
        var exp = new RegExp(/(%([%]|(\-)?(\+|\x20)?(0)?(\d+)?(\.(\d)?)?([bcdfosxX])))/g);
        var matches = new Array();
        var strings = new Array();
        var convCount = 0;
        var stringPosStart = 0;
        var stringPosEnd = 0;
        var matchPosEnd = 0;
        var newString = '';
        var match = null;

        while (match = exp.exec(string)) {
            if (match[9]) {
                convCount += 1;
            }

            stringPosStart = matchPosEnd;
            stringPosEnd = exp.lastIndex - match[0].length;
            strings[strings.length] = string.substring(stringPosStart, stringPosEnd);

            matchPosEnd = exp.lastIndex;
            matches[matches.length] = {
                match: match[0],
                left: match[3] ? true : false,
                sign: match[4] || '',
                pad: match[5] || ' ',
                min: match[6] || 0,
                precision: match[8],
                code: match[9] || '%',
                negative: parseInt(arguments[convCount]) < 0 ? true : false,
                argument: String(arguments[convCount])
            };
        }
        strings[strings.length] = string.substring(matchPosEnd);

        if (matches.length == 0) {
            return string;
        }
        if ((arguments.length - 1) < convCount) {
            return null;
        }

        var code = null;
        var match = null;
        var i = null;

        for (i=0; i<matches.length; i++) {

            if (matches[i].code == '%') {
                substitution = '%'
            }
            else if (matches[i].code == 'b') {
                matches[i].argument = String(Math.abs(parseInt(matches[i].argument)).toString(2));
                substitution = sprintfWrapper.convert(matches[i], true);
            }
            else if (matches[i].code == 'c') {
                matches[i].argument = String(String.fromCharCode(parseInt(Math.abs(parseInt(matches[i].argument)))));
                substitution = sprintfWrapper.convert(matches[i], true);
            }
            else if (matches[i].code == 'd') {
                matches[i].argument = String(Math.abs(parseInt(matches[i].argument)));
                substitution = sprintfWrapper.convert(matches[i]);
            }
            else if (matches[i].code == 'f') {
                matches[i].argument = String(Math.abs(parseFloat(matches[i].argument)).toFixed(matches[i].precision ? matches[i].precision : 6));
                substitution = sprintfWrapper.convert(matches[i]);
            }
            else if (matches[i].code == 'o') {
                matches[i].argument = String(Math.abs(parseInt(matches[i].argument)).toString(8));
                substitution = sprintfWrapper.convert(matches[i]);
            }
            else if (matches[i].code == 's') {
                matches[i].argument = matches[i].argument.substring(0, matches[i].precision ? matches[i].precision : matches[i].argument.length)
                substitution = sprintfWrapper.convert(matches[i], true);
            }
            else if (matches[i].code == 'x') {
                matches[i].argument = String(Math.abs(parseInt(matches[i].argument)).toString(16));
                substitution = sprintfWrapper.convert(matches[i]);
            }
            else if (matches[i].code == 'X') {
                matches[i].argument = String(Math.abs(parseInt(matches[i].argument)).toString(16));
                substitution = sprintfWrapper.convert(matches[i]).toUpperCase();
            }
            else {
                substitution = matches[i].match;
            }

            newString += strings[i];
            newString += substitution;

        }
        newString += strings[i];

        return newString;

    },

    convert : function(match, nosign){
        if (nosign) {
            match.sign = '';
        } else {
            match.sign = match.negative ? '-' : match.sign;
        }
        var l = match.min - match.argument.length + 1 - match.sign.length;
        var pad = new Array(l < 0 ? 0 : l).join(match.pad);
        if (!match.left) {
            if (match.pad == "0" || nosign) {
                return match.sign + pad + match.argument;
            } else {
                return pad + match.sign + match.argument;
            }
        } else {
            if (match.pad == "0" || nosign) {
                return match.sign + match.argument + pad.replace(/0/g, ' ');
            } else {
                return match.sign + match.argument + pad;
            }
        }
    }
}

var sprintf = sprintfWrapper.init;

function fl(key) {
    if (undefined === arguments || undefined === lang[key]) {
        return key;
    }
    arguments[0] = lang[key];
    return sprintf.apply(sprintf, arguments);
}
